// ========== Copyright Header Begin ==========================================
// 
// OpenSPARC T1 Processor File: FloatRegisterFile.cc
// Copyright (c) 2006 Sun Microsystems, Inc.  All Rights Reserved.
// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES.
// 
// The above named program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License version 2 as published by the Free Software Foundation.
// 
// The above named program is distributed in the hope that it will be 
// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
// 
// You should have received a copy of the GNU General Public
// License along with this work; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
// 
// ========== Copyright Header End ============================================
/************************************************************************
**  
**  Copyright (C) 2002, Sun Microsystems, Inc.
**
**  Sun considers its source code as an unpublished, proprietary
**  trade secret and it is available only under strict license provisions.
**  This copyright notice is placed here only to protect Sun in the event 
**  the source is deemed a published work. Disassembly, decompilation,
**  or other means of reducing the object code to human readable form
**  is prohibited by the license agreement under which this code is
**  provided to the user or company in possession of this copy."
**
*************************************************************************/
#include <FloatRegisterFile.h>
#include <Quad.h>
#include <StdMacros.h>
#include <strings.h>
#include <string.h>
#include <sstream>
#include <iomanip>
#include <cassert>
#include "CallbackInterface.h"

using namespace std;
using namespace Riesling;

const char* FloatRegisterFile::REGNUM_2_STRING[] = {
			"f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", 
			"f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15", 
			"f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23", 
			"f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31", 
			"f32", "f33", "f34", "f35", "f36", "f37", "f38", "f39", 
			"f40", "f41", "f42", "f43", "f44", "f45", "f46", "f47", 
			"f48", "f49", "f50", "f51", "f52", "f53", "f54", "f55", 
			"f56", "f57", "f58", "f59", "f60", "f61", "f62", "f63"
		    };


///////////////////////////////////////////////////////////////

FloatRegisterFile::FloatRegisterFile( uint_t singleFloats, uint_t doubleFloats, uint_t quadFloats ) :
    singleFloats_(singleFloats),
    doubleFloats_(doubleFloats),
    quadFloats_(quadFloats)
{
    int   count;

    if (singleFloats_ != DEFAULT_SP_FLOAT_REG_SET_SIZE) {
	RIESLING_THROW_SPARC_ARCH_VIOLATION( 
		"single precision floats != 32",
	        "Sparc Architecture Manual, Section 5.1.4"
		);
    }

    if (doubleFloats_ != DEFAULT_DP_FLOAT_REG_SET_SIZE) {
	RIESLING_THROW_SPARC_ARCH_VIOLATION( 
		"double precision floats != 32",
	        "Sparc Architecture Manual, Section 5.1.4"
		);
    }

    if (quadFloats_ != DEFAULT_QP_FLOAT_REG_SET_SIZE) {
	RIESLING_THROW_SPARC_ARCH_VIOLATION( 
		"quad precision floats != 16",
	        "Sparc Architecture Manual, Section 5.1.4"
		);
    }

    floatRegsCount_ = quadFloats_ * 4;

    count = doubleFloats_ * 2;
    if (floatRegsCount_ < count)
       floatRegsCount_ = count;

    count = singleFloats_;
    if (floatRegsCount_ < count)
       floatRegsCount_ = count;

    floatRegs_ = new FloatRegisterT[floatRegsCount_];
    floateccregs_ = new uint32_t[floatRegsCount_];

    bzero( floatRegs_, sizeof( FloatRegisterT ) * floatRegsCount_ );
    bzero( floateccregs_, sizeof( uint32_t ) * floatRegsCount_ );
}

///////////////////////////////////////////////////////////////

FloatRegisterFile::FloatRegisterFile( const FloatRegisterFile &orig ) :
    singleFloats_(orig.singleFloats_),
    doubleFloats_(orig.doubleFloats_),
    quadFloats_(orig.quadFloats_),
    floatRegsCount_(orig.floatRegsCount_)
{
    if (singleFloats_ != DEFAULT_SP_FLOAT_REG_SET_SIZE) {
	RIESLING_THROW_SPARC_ARCH_VIOLATION( 
		"single precision floats != 32",
	        "Sparc Architecture Manual, Section 5.1.4"
		);
    }

    if (doubleFloats_ != DEFAULT_DP_FLOAT_REG_SET_SIZE) {
	RIESLING_THROW_SPARC_ARCH_VIOLATION( 
		"double precision floats != 32",
	        "Sparc Architecture Manual, Section 5.1.4"
		);
    }

    if (quadFloats_ != DEFAULT_QP_FLOAT_REG_SET_SIZE) {
	RIESLING_THROW_SPARC_ARCH_VIOLATION( 
		"quad precision floats != 16",
	        "Sparc Architecture Manual, Section 5.1.4"
		);
    }

    floatRegs_ = new FloatRegisterT[floatRegsCount_];
    floateccregs_ = new uint32_t[floatRegsCount_];
    memcpy( floatRegs_, orig.floatRegs_, sizeof( FloatRegisterT ) * floatRegsCount_ );
    memcpy( floateccregs_, orig.floateccregs_, sizeof( uint32_t ) * floatRegsCount_ );
}

///////////////////////////////////////////////////////////////

FloatRegisterFile::~FloatRegisterFile()
{
    delete [] floatRegs_;
    delete [] floateccregs_;
}

///////////////////////////////////////////////////////////////

bool FloatRegisterFile::operator==( const FloatRegisterFile &rhs ) const
{
    bool   result;

    result = false;

    if ( (singleFloats_ == rhs.singleFloats_) &&
	 (doubleFloats_ == rhs.doubleFloats_) &&
	 (quadFloats_ == rhs.quadFloats_)     &&
	 (floatRegsCount_ == rhs.floatRegsCount_) ) {
	
	if (( memcmp( floatRegs_, rhs.floatRegs_, sizeof(FloatRegisterT ) * floatRegsCount_) == 0 ) &&
	    ( memcmp( floateccregs_, rhs.floateccregs_, sizeof(uint32_t ) * floatRegsCount_) == 0 ))
	    result = true;
    }

    return result;
}

///////////////////////////////////////////////////////////////

const FloatRegisterFile & FloatRegisterFile::operator=( const FloatRegisterFile &rhs )
{
    if ( this != &rhs ) {

	if ( floatRegsCount_ != rhs.floatRegsCount_ ) {
	    delete [] floatRegs_;
            delete [] floateccregs_;
	    floatRegs_      = new FloatRegisterT[rhs.floatRegsCount_];
            floateccregs_ = new uint32_t[rhs.floatRegsCount_];
	    floatRegsCount_ = rhs.floatRegsCount_;
	}

	singleFloats_ = rhs.singleFloats_;
	doubleFloats_ = rhs.doubleFloats_;
	quadFloats_   = rhs.quadFloats_;

	memcpy( floatRegs_, rhs.floatRegs_, sizeof( FloatRegisterT ) * rhs.floatRegsCount_ );
        memcpy( floateccregs_, rhs.floateccregs_, sizeof( uint32_t ) * rhs.floatRegsCount_ );
    }

    return *this;
}


///////////////////////////////////////////////////////////////

uint32_t FloatRegisterFile::getSpfp( uint32_t reg ) const
{
    //Manipulate 'reg' operand
    reg = reg % singleFloats_;

    //Verify ECC
    if (CallbackInterface::getEnableRAS())
    {
      verifyECC(0,reg);
    }

    //Return the requested value
    return floatRegs_[reg];
}

float FloatRegisterFile::getFloat( uint32_t reg ) const
{
    //Declare local variables
    float      fvalue;
    uint32_t   ivalue;

    //Manipulate 'reg' operand
    reg    = reg % singleFloats_;

    //Verify ECC
    if (CallbackInterface::getEnableRAS())
    {
      verifyECC(0,reg);
    }

    //Type-cast the return value
    ivalue = floatRegs_[reg];
    fvalue = *(float *) &ivalue;

    //Return the requested value
    return fvalue;
}

uint64_t FloatRegisterFile::getDpfp( uint32_t reg ) const
{
    //Declare local variables
    uint64_t   lvalue;

    //Manipulate 'reg' operand
    reg = reg % (doubleFloats_ * 2);

    if (reg & 0x1) {
	reg = reg & ~0x1;
	reg = reg | ( 1 << 5 );
    }

    //Verify ECC
    if (CallbackInterface::getEnableRAS())
    {
      verifyECC(1,reg);
    }

    //Type-cast the return value
    lvalue = *(uint64_t *) &floatRegs_[reg];

#ifdef LITTLE_ENDIAN
    lvalue = (lvalue & 0xffffffffULL) << 32 |
             (lvalue >> 32);
#endif

    //Return the requested value
    return lvalue;
}

double FloatRegisterFile::getDouble( uint32_t reg ) const
{
    //Declare local variables
    uint64_t   lvalue;
    double     dvalue;

    //Manipulate 'reg' operand
    reg = reg % (doubleFloats_ * 2);

    if (reg & 0x1) {
	reg = reg & ~0x1;
	reg = reg | ( 1 << 5 );
    }

    //Verify ECC
    if (CallbackInterface::getEnableRAS())
    {
      verifyECC(1,reg);
    }

    //Type-cast the return value
    lvalue = *(uint64_t *) &floatRegs_[reg];
#ifdef LITTLE_ENDIAN
    lvalue = (lvalue & 0xffffffffULL) << 32 |
             (lvalue >> 32);
#endif
    dvalue = *(double *) &lvalue;

    //Return the requested value
    return dvalue;
}

uint64_t FloatRegisterFile::getQuadHigh( uint32_t reg ) const
{
    //Declare local variables
    uint64_t   quadHigh;

    //Manipulate 'reg' operand
    reg = reg % (doubleFloats_ * 2);

    if (reg & 0x1) {
	reg = reg & ~0x3;
	reg = reg | ( 1 << 5 );
    } else {
	reg = reg & ~0x3;
    }

    //Verify ECC
    if (CallbackInterface::getEnableRAS())
    {
      verifyECC(1,reg);
    }

    //Type-cast the return value
    quadHigh = *(uint64_t *) &floatRegs_[reg];
#ifdef LITTLE_ENDIAN
    quadHigh = (quadHigh & 0xffffffffULL) << 32 |
             (quadHigh >> 32);
#endif

    //Return the requested value
    return quadHigh;
}

uint64_t FloatRegisterFile::getQuadLow( uint32_t reg ) const
{
    //Declare local variables
    uint64_t   quadLow;

    //Manipulate 'reg' operand
    reg = reg % (doubleFloats_ * 2);

    if (reg & 0x1) {
	reg = reg & ~0x3;
	reg = reg | ( 1 << 5 );
    } else {
	reg = reg & ~0x3;
    }

    //Verify ECC
    if (CallbackInterface::getEnableRAS())
    {
      verifyECC(2,reg);
    }

    //Type-cast the return value
    quadLow = *(uint64_t *) &floatRegs_[reg+2];
#ifdef LITTLE_ENDIAN
    quadLow = (quadLow & 0xffffffffULL) << 32 |
             (quadLow >> 32);
#endif

    //Return the requested value
    return quadLow;
}

///////////////////////////////////////////////////////////////

void FloatRegisterFile::setSpfp( uint32_t reg, uint32_t ivalue )
{
    //Manipulate 'reg' operand
    reg = reg % singleFloats_;

    //Set the value for the specified register
    floatRegs_[reg] = ivalue;

    //Assign ECC for the register(pair)
    if (CallbackInterface::getEnableRAS())
    {
      assignECC(0,reg);
    }
}

void FloatRegisterFile::setFloat( uint32_t reg, float fvalue )
{
    //Declare local variables
    uint32_t   ivalue;

    //Manipulate 'reg' operand
    reg    = reg % singleFloats_;

    //Set the value for the specified register
    ivalue = *(uint32_t *) &fvalue;
    floatRegs_[reg] = ivalue;

    //Assign ECC for the register(pair)
    if (CallbackInterface::getEnableRAS())
    {
      assignECC(0,reg);
    }
}

void FloatRegisterFile::setDpfp( uint32_t reg, uint64_t lvalue )
{
    //Manipulate 'reg' operand
    reg = reg % (doubleFloats_ * 2);

    if (reg & 0x1) {
	reg = reg & ~0x1;
	reg = reg | ( 1 << 5 );
    }

    //Set the value for the specified register
#ifdef LITTLE_ENDIAN
    lvalue = (lvalue & 0xffffffffULL) << 32 |
             (lvalue >> 32);
#endif
    *(uint64_t *) &floatRegs_[reg] = lvalue;

    //Assign ECC for the register(pair)
    if (CallbackInterface::getEnableRAS())
    {
      assignECC(1,reg);
    }
}

void FloatRegisterFile::setDouble( uint32_t reg, double dvalue )
{
    //Declare local variables
    uint64_t   lvalue;

    //Manipulate 'reg' operand
    reg = reg % (doubleFloats_ * 2);

    if (reg & 0x1) {
	reg = reg & ~0x1;
	reg = reg | ( 1 << 5 );
    }

    //Set the value for the specified register
    lvalue = *(uint64_t *) &dvalue;
#ifdef LITTLE_ENDIAN
    lvalue = (lvalue & 0xffffffffULL) << 32 |
             (lvalue >> 32);
#endif
    *(uint64_t *) &floatRegs_[reg] = lvalue;

    //Assign ECC for the register(pair)
    if (CallbackInterface::getEnableRAS())
    {
      assignECC(1,reg);
    }
}

void FloatRegisterFile::setQuadHigh( uint32_t reg, uint64_t quadHigh )
{
    //Manipulate 'reg' operand
    reg = reg % (doubleFloats_ * 2);

    if (reg & 0x1) {
	reg = reg & ~0x3;
	reg = reg | ( 1 << 5 );
    } else {
	reg = reg & ~0x3;
    }

    //Set the value for the specified register
#ifdef LITTLE_ENDIAN
    quadHigh = (quadHigh & 0xffffffffULL) << 32 |
             (quadHigh >> 32);
#endif
    *(uint64_t *) &floatRegs_[reg] = quadHigh;

    //Assign ECC for the register(pair)
    if (CallbackInterface::getEnableRAS())
    {
      assignECC(1,reg);
    }
}

void FloatRegisterFile::setQuadLow( uint32_t reg, uint64_t quadLow )
{
    //Manipulate 'reg' operand
    reg = reg % (doubleFloats_ * 2);

    if (reg & 0x1) {
	reg = reg & ~0x3;
	reg = reg | ( 1 << 5 );
    } else {
	reg = reg & ~0x3;
    }

    //Set the value for the specified register
#ifdef LITTLE_ENDIAN
    quadLow = (quadLow & 0xffffffffULL) << 32 |
             (quadLow >> 32);
#endif
    *(uint64_t *) &floatRegs_[reg+2] = quadLow;

    //Assign ECC for the register(pair)
    if (CallbackInterface::getEnableRAS())
    {
      assignECC(2,reg);
    }
}

///////////////////////////////////////////////////////////////

uint64_t FloatRegisterFile::getECC(uint32_t reg) const
{
  return floateccregs_[reg];
}

void FloatRegisterFile::setECC(uint32_t reg, uint32_t value) 
{
  floateccregs_[reg] = value;
}

///////////////////////////////////////////////////////////////

string
FloatRegisterFile::toString() const
{
    ostringstream os;

    for ( int index = 0; index < floatRegsCount_; ++index ) {
	if ( index && ((index % 4) == 0) ) {
	    os << "\n";
	} else {
	    if (index > 0)
		os << " ";
	}
	if (index < 10)
	    os << " " << REGNUM_2_STRING[index] << "=0x" << hex << setw(8) << setfill('0') << floatRegs_[index];
	else
	    os << REGNUM_2_STRING[index] << "=0x" << hex << setw(8) << setfill('0') << floatRegs_[index]; 
    }

    os << "\n";

    return os.str();
}

///////////////////////////////////////////////////////////////

void
FloatRegisterFile::print( ostream& os ) const
{
    os << toString();
}

///////////////////////////////////////////////////////////////

void 
FloatRegisterFile::snapshot( SS_SnapShot &ss, const char *prefix )
{
    // char extended_prefix[SS_SnapShot::LINE_SIZE];

    sprintf(ss.tag,"%s.singleFloats",prefix); ss.val(&singleFloats_);
    sprintf(ss.tag,"%s.doubleFloats",prefix); ss.val(&doubleFloats_);
    sprintf(ss.tag,"%s.quadFloats",prefix); ss.val(&quadFloats_);
    sprintf(ss.tag,"%s.floatRegsCount",prefix); ss.val(&floatRegsCount_);

		// FloatRegisterT *floatRegs_;
    for ( int index = 0; index < floatRegsCount_; ++index ) {
	sprintf(ss.tag,"%s.fpreg%d",prefix,index); ss.val(&(floatRegs_[index]));
    }
}

///////////////////////////////////////////////////////////////

void 
FloatRegisterFile::doRandomInit()
{
    for ( int index = 0; index < floatRegsCount_; ++index ) {
	floatRegs_[index] = rand();
    }
}
